package main

import (
	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/sys/unix"
	"gopkg.in/natefinch/lumberjack.v2"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"
	"syscall"
)

var logger *logrus.Logger

type MmapWriter struct {
	file    *os.File
	data    []byte
	offset  int64
	maxSize int64
	mutex   sync.Mutex
}

// 初始化日志
func initLogger(logConfig LogConfig) {

	// 检查日志存储路径, 如果没有设置，则默认使用 ./logs/qsbank_proxy.log
	if logConfig.LogSavePath == "" {
		logConfig.LogSavePath = "./logs/qsbank_proxy.log"
	}

	// 确保日志目录存在
	logDir := filepath.Dir(logConfig.LogSavePath)
	if err := os.MkdirAll(logDir, 0755); err != nil {
		log.Fatalf("无法创建日志目录: %v", err)
	}

	// 创建日志对象
	logger = logrus.New()

	// 设置日志级别
	level, err := logrus.ParseLevel(logConfig.LogLevel)
	if err != nil {
		level = logrus.InfoLevel
	}
	logger.SetLevel(level)

	// 设置日志格式
	logger.SetFormatter(&JavaFormatter{
		EnablePackageAbbrev: false,
		isTerminal:          terminal.IsTerminal(int(os.Stdout.Fd())),
		EnableColor:         terminal.IsTerminal(int(os.Stdout.Fd())),
	})

	// 创建MMAP writer
	mmapWriter, err := NewMmapWriter(logConfig.LogSavePath)
	if err != nil {
		log.Fatalf("创建MMAP写入器失败: %v", err)
	}
	logger.SetOutput(io.MultiWriter(
		os.Stdout,
		&lumberjack.Logger{
			// 保留原有日志轮转配置
			Filename:  logConfig.LogSavePath,
			MaxAge:    logConfig.MaxDays,
			LocalTime: true,
			MaxSize:   50,
			Compress:  true,
		},
		mmapWriter,
	))

	logger.SetReportCaller(true)

	// 注册退出时的清理函数
	go func() {
		<-make(chan os.Signal, 1)
		mmapWriter.Close()
		os.Exit(0)
	}()
}

func NewMmapWriter(path string) (*MmapWriter, error) {
	// 打开或创建日志文件
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return nil, err
	}

	// 获取当前文件大小
	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}
	fileSize := fi.Size()

	// 初始映射大小设置为1GB（可根据需要调整）
	initialSize := int64(1 << 30)
	if fileSize == 0 {
		if err := f.Truncate(initialSize); err != nil {
			return nil, err
		}
		fileSize = initialSize
	}

	// 内存映射
	data, err := unix.Mmap(int(f.Fd()), 0, int(fileSize),
		syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		return nil, err
	}

	return &MmapWriter{
		file: f,
		data: data,
		// 从文件末尾开始写入
		offset:  fi.Size(),
		maxSize: fileSize,
	}, nil
}

// 实现io.Writer接口
func (m *MmapWriter) Write(p []byte) (n int, err error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	// 检查剩余空间
	remaining := m.maxSize - m.offset
	if remaining < int64(len(p)) {
		// 扩展文件大小（每次扩展1GB）
		newSize := m.maxSize + (1 << 30)
		if err := m.file.Truncate(newSize); err != nil {
			return 0, err
		}

		// 重新映射
		unix.Munmap(m.data)
		data, err := unix.Mmap(int(m.file.Fd()), 0, int(newSize),
			syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
		if err != nil {
			return 0, err
		}

		m.data = data
		m.maxSize = newSize
	}

	// 写入数据
	n = copy(m.data[m.offset:], p)
	m.offset += int64(n)
	return n, nil
}

func (m *MmapWriter) Close() error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	// 同步数据到磁盘
	if err := unix.Msync(m.data, syscall.MS_SYNC); err != nil {
		return err
	}

	// 解除内存映射
	if err := unix.Munmap(m.data); err != nil {
		return err
	}

	// 调整文件到实际大小
	if err := m.file.Truncate(m.offset); err != nil {
		return err
	}

	return m.file.Close()
}
